home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / util / gnu / groff_src.lha / Groff-1.07 / tbl / main.cc next >
C/C++ Source or Header  |  1992-11-11  |  31KB  |  1,486 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include "table.h"
  22.  
  23. #define MAX_POINT_SIZE 99
  24. #define MAX_VERTICAL_SPACING 72
  25.  
  26. static int compatible_flag = 0;
  27.  
  28. class table_input {
  29.   FILE *fp;
  30.   enum { START, MIDDLE, REREAD_T, REREAD_TE, REREAD_E, END, ERROR } state;
  31.   string unget_stack;
  32. public:
  33.   table_input(FILE *);
  34.   int get();
  35.   int ended() { return unget_stack.empty() && state == END; }
  36.   void unget(char);
  37. };
  38.  
  39. table_input::table_input(FILE *p)
  40. : fp(p), state(START)
  41. {
  42. }
  43.  
  44. void table_input::unget(char c)
  45. {
  46.   assert(c != '\0');
  47.   unget_stack += c;
  48.   if (c == '\n')
  49.     current_lineno--;
  50. }
  51.  
  52. int table_input::get()
  53. {
  54.   int len = unget_stack.length();
  55.   if (len != 0) {
  56.     unsigned char c = unget_stack[len - 1];
  57.     unget_stack.set_length(len - 1);
  58.     if (c == '\n')
  59.       current_lineno++;
  60.     return c;
  61.   }
  62.   int c;
  63.   for (;;) {
  64.     switch (state) {
  65.     case START:
  66.       if ((c = getc(fp)) == '.') {
  67.     if ((c = getc(fp)) == 'T') {
  68.       if ((c = getc(fp)) == 'E') {
  69.         if (compatible_flag) {
  70.           state = END;
  71.           return EOF;
  72.         }
  73.         else {
  74.           c = getc(fp);
  75.           if (c != EOF)
  76.         ungetc(c, fp);
  77.           if (c == EOF || c == ' ' || c == '\n') {
  78.         state = END;
  79.         return EOF;
  80.           }
  81.           state = REREAD_TE;
  82.           return '.';
  83.         }
  84.       }
  85.       else {
  86.         if (c != EOF)
  87.           ungetc(c, fp);
  88.         state = REREAD_T;
  89.         return '.';
  90.       }
  91.     }
  92.     else {
  93.       if (c != EOF)
  94.         ungetc(c, fp);
  95.       state = MIDDLE;
  96.       return '.';
  97.     }
  98.       }
  99.       else if (c == EOF) {
  100.     state = ERROR;
  101.     return EOF;
  102.       }
  103.       else {
  104.     if (c == '\n')
  105.       current_lineno++;
  106.     else {
  107.       state = MIDDLE;
  108.       if (c == '\0') {
  109.         error("illegal input character code 0");
  110.         break;
  111.       }
  112.     }
  113.     return c;
  114.       }
  115.       break;
  116.     case MIDDLE:
  117.       // handle line continuation
  118.       if ((c = getc(fp)) == '\\') {
  119.     c = getc(fp);
  120.     if (c == '\n')
  121.       c = getc(fp);        // perhaps state ought to be START now
  122.     else {
  123.       if (c != EOF)
  124.         ungetc(c, fp);
  125.       c = '\\';
  126.     }
  127.       }
  128.       if (c == EOF) {
  129.     state = ERROR;
  130.     return EOF;
  131.       }
  132.       else {
  133.     if (c == '\n') {
  134.       state = START;
  135.       current_lineno++;
  136.     }
  137.     else if (c == '\0') {
  138.       error("illegal input character code 0");
  139.       break;
  140.     }
  141.     return c;
  142.       }
  143.     case REREAD_T:
  144.       state = MIDDLE;
  145.       return 'T';
  146.     case REREAD_TE:
  147.       state = REREAD_E;
  148.       return 'T';
  149.     case REREAD_E:
  150.       state = MIDDLE;
  151.       return 'E';
  152.     case END:
  153.     case ERROR:
  154.       return EOF;
  155.     }
  156.   }
  157. }
  158.  
  159. void process_input_file(FILE *);
  160. void process_table(table_input &in);
  161.  
  162. void process_input_file(FILE *fp)
  163. {
  164.   enum { START, MIDDLE, HAD_DOT, HAD_T, HAD_TS, HAD_l, HAD_lf } state;
  165.   state = START;
  166.   int c;
  167.   while ((c = getc(fp)) != EOF)
  168.     switch (state) {
  169.     case START:
  170.       if (c == '.')
  171.     state = HAD_DOT;
  172.       else {
  173.     if (c == '\n')
  174.       current_lineno++;
  175.     else
  176.       state = MIDDLE;
  177.     putchar(c);
  178.       }
  179.       break;
  180.     case MIDDLE:
  181.       if (c == '\n') {
  182.     current_lineno++;
  183.     state = START;
  184.       }
  185.       putchar(c);
  186.       break;
  187.     case HAD_DOT:
  188.       if (c == 'T')
  189.     state = HAD_T;
  190.       else if (c == 'l')
  191.     state = HAD_l;
  192.       else {
  193.     putchar('.');
  194.     putchar(c);
  195.     if (c == '\n') {
  196.       current_lineno++;
  197.       state = START;
  198.     }
  199.     else
  200.       state = MIDDLE;
  201.       }
  202.       break;
  203.     case HAD_T:
  204.       if (c == 'S')
  205.     state = HAD_TS;
  206.       else {
  207.     putchar('.');
  208.     putchar('T');
  209.     putchar(c);
  210.     if (c == '\n') {
  211.        current_lineno++;
  212.       state = START;
  213.     }
  214.     else
  215.       state = MIDDLE;
  216.       }
  217.       break;
  218.     case HAD_TS:
  219.       if (c == ' ' || c == '\n' || compatible_flag) {
  220.     putchar('.');
  221.     putchar('T');
  222.     putchar('S');
  223.     while (c != '\n') {
  224.       if (c == EOF) {
  225.         error("end of file at beginning of table");
  226.         return;
  227.       }
  228.       putchar(c);
  229.       c = getc(fp);
  230.     }
  231.     putchar('\n');
  232.     current_lineno++;
  233.     {
  234.       table_input input(fp);
  235.       process_table(input);
  236.       set_troff_location(current_filename, current_lineno);
  237.       if (input.ended()) {
  238.         fputs(".TE", stdout);
  239.         while ((c = getc(fp)) != '\n') {
  240.           if (c == EOF) {
  241.         putchar('\n');
  242.         return;
  243.           }
  244.           putchar(c);
  245.         }
  246.         putchar('\n');
  247.         current_lineno++;
  248.       }
  249.     }
  250.     state = START;
  251.       }
  252.       else {
  253.     fputs(".TS", stdout);
  254.     putchar(c);
  255.     state = MIDDLE;
  256.       }
  257.       break;
  258.     case HAD_l:
  259.       if (c == 'f')
  260.     state = HAD_lf;
  261.       else {
  262.     putchar('.');
  263.     putchar('l');
  264.     putchar(c);
  265.     if (c == '\n') {
  266.        current_lineno++;
  267.       state = START;
  268.     }
  269.     else
  270.       state = MIDDLE;
  271.       }
  272.       break;
  273.     case HAD_lf:
  274.       if (c == ' ' || c == '\n' || compatible_flag) {
  275.     string line;
  276.     while (c != EOF) {
  277.       line += c;
  278.       if (c == '\n') {
  279.         current_lineno++;
  280.         break;
  281.       }
  282.       c = getc(fp);
  283.     }
  284.     line += '\0';
  285.     interpret_lf_args(line.contents());
  286.     printf(".lf%s", line.contents());
  287.     state = START;
  288.       }
  289.       else {
  290.     fputs(".lf", stdout);
  291.     putchar(c);
  292.     state = MIDDLE;
  293.       }
  294.       break;
  295.     default:
  296.       assert(0);
  297.     }
  298.   switch(state) {
  299.   case START:
  300.     break;
  301.   case MIDDLE:
  302.     putchar('\n');
  303.     break;
  304.   case HAD_DOT:
  305.     fputs(".\n", stdout);
  306.     break;
  307.   case HAD_l:
  308.     fputs(".l\n", stdout);
  309.     break;
  310.   case HAD_T:
  311.     fputs(".T\n", stdout);
  312.     break;
  313.   case HAD_lf:
  314.     fputs(".lf\n", stdout);
  315.     break;
  316.   case HAD_TS:
  317.     fputs(".TS\n", stdout);
  318.     break;
  319.   }
  320.   if (fp != stdin)
  321.     fclose(fp);
  322. }
  323.  
  324. struct options {
  325.   unsigned flags;
  326.   int linesize;
  327.   char delim[2];
  328.   char tab_char;
  329.  
  330.   options();
  331. };
  332.  
  333. options::options()
  334. : flags(0), tab_char('\t'), linesize(0)
  335. {
  336.   delim[0] = delim[1] = '\0';
  337. }
  338.  
  339. // Return non-zero if p and q are the same ignoring case.
  340.  
  341. int strieq(const char *p, const char *q)
  342. {
  343.   for (; cmlower(*p) == cmlower(*q); p++, q++)
  344.     if (*p == '\0')
  345.       return 1;
  346.   return 0;
  347. }
  348.  
  349. // return 0 if we should give up in this table
  350.  
  351. options *process_options(table_input &in)
  352. {
  353.   options *opt = new options;
  354.   string line;
  355.   int level = 0;
  356.   for (;;) {
  357.     int c = in.get();
  358.     if (c == EOF) {
  359.       int i = line.length();
  360.       while (--i >= 0)
  361.     in.unget(line[i]);
  362.       return opt;
  363.     }
  364.     if (c == '\n') {
  365.       in.unget(c);
  366.       int i = line.length();
  367.       while (--i >= 0)
  368.     in.unget(line[i]);
  369.       return opt;
  370.     }
  371.     else if (c == '(')
  372.       level++;
  373.     else if (c == ')')
  374.       level--;
  375.     else if (c == ';' && level == 0) {
  376.       line += '\0';
  377.       break;
  378.     }
  379.     line += c;
  380.   }
  381.   if (line.empty())
  382.     return opt;
  383.   char *p = &line[0];
  384.   for (;;) {
  385.     while (csspace(*p) || *p == ',')
  386.       p++;
  387.     if (*p == '\0')
  388.       break;
  389.     char *q = p;
  390.     while (*q != ' ' && *q != '\0' && *q != '\t' && *q != ',' && *q != '(')
  391.       q++;
  392.     char *arg = 0;
  393.     if (*q != '(' && *q != '\0')
  394.       *q++ = '\0';
  395.     while (csspace(*q))
  396.       q++;
  397.     if (*q == '(') {
  398.       *q++ = '\0';
  399.       arg = q;
  400.       while (*q != ')' && *q != '\0')
  401.     q++;
  402.       if (*q == '\0')
  403.     error("missing `)'");
  404.       else
  405.     *q++ = '\0';
  406.     }
  407.     if (*p == '\0') {
  408.       if (arg)
  409.     error("argument without option");
  410.     }
  411.     else if (strieq(p, "tab")) {
  412.       if (!arg)
  413.     error("`tab' option requires argument in parentheses");
  414.       else {
  415.     if (arg[0] == '\0' || arg[1] != '\0')
  416.       error("argument to `tab' option must be a single character");
  417.     else
  418.       opt->tab_char = arg[0];
  419.       }
  420.     }
  421.     else if (strieq(p, "linesize")) {
  422.       if (!arg)
  423.     error("`linesize' option requires argument in parentheses");
  424.       else {
  425.     if (sscanf(arg, "%d", &opt->linesize) != 1)
  426.       error("bad linesize `%s'", arg);
  427.     else if (opt->linesize <= 0) {
  428.       error("linesize must be positive");
  429.       opt->linesize = 0;
  430.     }
  431.       }
  432.     }
  433.     else if (strieq(p, "delim")) {
  434.       if (!arg)
  435.     error("`delim' option requires argument in parentheses");
  436.       else if (arg[0] == '\0' || arg[1] == '\0' || arg[2] != '\0')
  437.     error("argument to `delim' option must be two characters");
  438.       else {
  439.     opt->delim[0] = arg[0];
  440.     opt->delim[1] = arg[1];
  441.       }
  442.     }
  443.     else if (strieq(p, "center") || strieq(p, "centre")) {
  444.       if (arg)
  445.     error("`center' option does not take a argument");
  446.       opt->flags |= table::CENTER;
  447.     }
  448.     else if (strieq(p, "expand")) {
  449.       if (arg)
  450.     error("`expand' option does not take a argument");
  451.       opt->flags |= table::EXPAND;
  452.     }
  453.     else if (strieq(p, "box") || strieq(p, "frame")) {
  454.       if (arg)
  455.     error("`box' option does not take a argument");
  456.       opt->flags |= table::BOX;
  457.     }
  458.     else if (strieq(p, "doublebox") || strieq(p, "doubleframe")) {
  459.       if (arg)
  460.     error("`doublebox' option does not take a argument");
  461.       opt->flags |= table::DOUBLEBOX;
  462.     }
  463.     else if (strieq(p, "allbox")) {
  464.       if (arg)
  465.     error("`allbox' option does not take a argument");
  466.       opt->flags |= table::ALLBOX;
  467.     }
  468.     else if (strieq(p, "nokeep")) {
  469.       if (arg)
  470.     error("`nokeep' option does not take a argument");
  471.       opt->flags |= table::NOKEEP;
  472.     }
  473.     else {
  474.       error("unrecognised global option `%1'", p);
  475.       // delete opt;
  476.       // return 0;
  477.     }
  478.     p = q;
  479.   }
  480.   return opt;
  481. }
  482.  
  483. entry_modifier::entry_modifier()
  484. : vertical_alignment(CENTER), zero_width(0), stagger(0)
  485. {
  486.   vertical_spacing.inc = vertical_spacing.val = 0;
  487.   point_size.inc = point_size.val = 0;
  488. }
  489.  
  490. entry_modifier::~entry_modifier()
  491. {
  492. }
  493.  
  494. entry_format::entry_format() : type(FORMAT_LEFT)
  495. {
  496. }
  497.  
  498. entry_format::entry_format(format_type t) : type(t)
  499. {
  500. }
  501.  
  502. void entry_format::debug_print() const
  503. {
  504.   switch (type) {
  505.   case FORMAT_LEFT:
  506.     putc('l', stderr);
  507.     break;
  508.   case FORMAT_CENTER:
  509.     putc('c', stderr);
  510.     break;
  511.   case FORMAT_RIGHT:
  512.     putc('r', stderr);
  513.     break;
  514.   case FORMAT_NUMERIC:
  515.     putc('n', stderr);
  516.     break;
  517.   case FORMAT_ALPHABETIC:
  518.     putc('a', stderr);
  519.     break;
  520.   case FORMAT_SPAN:
  521.     putc('s', stderr);
  522.     break;
  523.   case FORMAT_VSPAN:
  524.     putc('^', stderr);
  525.     break;
  526.   case FORMAT_HLINE:
  527.     putc('_', stderr);
  528.     break;
  529.   case FORMAT_DOUBLE_HLINE:
  530.     putc('=', stderr);
  531.     break;
  532.   default:
  533.     assert(0);
  534.     break;
  535.   }
  536.   if (point_size.val != 0) {
  537.     putc('p', stderr);
  538.     if (point_size.inc > 0)
  539.       putc('+', stderr);
  540.     else if (point_size.inc < 0)
  541.       putc('-', stderr);
  542.     fprintf(stderr, "%d ", point_size.val);
  543.   }
  544.   if (vertical_spacing.val != 0) {
  545.     putc('v', stderr);
  546.     if (vertical_spacing.inc > 0)
  547.       putc('+', stderr);
  548.     else if (vertical_spacing.inc < 0)
  549.       putc('-', stderr);
  550.     fprintf(stderr, "%d ", vertical_spacing.val);
  551.   }
  552.   if (!font.empty()) {
  553.     putc('f', stderr);
  554.     put_string(font, stderr);
  555.     putc(' ', stderr);
  556.   }
  557.   switch (vertical_alignment) {
  558.   case entry_modifier::CENTER:
  559.     break;
  560.   case entry_modifier::TOP:
  561.     putc('t', stderr);
  562.     break;
  563.   case entry_modifier::BOTTOM:
  564.     putc('d', stderr);
  565.     break;
  566.   }
  567.   if (zero_width)
  568.     putc('z', stderr);
  569.   if (stagger)
  570.     putc('u', stderr);
  571. }
  572.  
  573. struct format {
  574.   int nrows;
  575.   int ncolumns;
  576.   int *separation;
  577.   string *width;
  578.   char *equal;
  579.   entry_format **entry;
  580.   char **vline;
  581.  
  582.   format(int nr, int nc);
  583.   ~format();
  584.   void add_rows(int n);
  585. };
  586.  
  587. format::format(int nr, int nc) : nrows(nr), ncolumns(nc)
  588. {
  589.   int i;
  590.   separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
  591.   for (i = 0; i < ncolumns-1; i++)
  592.     separation[i] = -1;
  593.   width = new string[ncolumns];
  594.   equal = new char[ncolumns];
  595.   for (i = 0; i < ncolumns; i++)
  596.     equal[i] = 0;
  597.   entry = new entry_format *[nrows];
  598.   for (i = 0; i < nrows; i++)
  599.     entry[i] = new entry_format[ncolumns];
  600.   vline = new char*[nrows];
  601.   for (i = 0; i < nrows; i++) {
  602.     vline[i] = new char[ncolumns+1];
  603.     for (int j = 0; j < ncolumns+1; j++)
  604.       vline[i][j] = 0;
  605.   }
  606. }
  607.  
  608. void format::add_rows(int n)
  609. {
  610.   int i;
  611.   char **old_vline = vline;
  612.   vline = new char*[nrows + n];
  613.   for (i = 0; i < nrows; i++)
  614.     vline[i] = old_vline[i];
  615.   a_delete old_vline;
  616.   for (i = 0; i < n; i++) {
  617.     vline[nrows + i] = new char[ncolumns + 1];
  618.     for (int j = 0; j < ncolumns + 1; j++)
  619.       vline[nrows + i][j] = 0;
  620.   }
  621.   entry_format **old_entry = entry;
  622.   entry = new entry_format *[nrows + n];
  623.   for (i = 0; i < nrows; i++)
  624.     entry[i] = old_entry[i];
  625.   a_delete old_entry;
  626.   for (i = 0; i < n; i++)
  627.     entry[nrows + i] = new entry_format[ncolumns];
  628.   nrows += n;
  629. }
  630.  
  631. format::~format()
  632. {
  633.   a_delete separation;
  634.   ad_delete(ncolumns) width;
  635.   a_delete equal;
  636.   for (int i = 0; i < nrows; i++) {
  637.     a_delete vline[i];
  638.     ad_delete(ncolumns) entry[i];
  639.   }
  640.   a_delete vline;
  641.   a_delete entry;
  642. }
  643.  
  644. struct input_entry_format : entry_format {
  645.   input_entry_format *next;
  646.   string width;
  647.   int separation;
  648.   int vline;
  649.   int pre_vline;
  650.   int last_column;
  651.   int equal;
  652.   input_entry_format(format_type, input_entry_format * = 0);
  653.   ~input_entry_format();
  654.   void debug_print();
  655. };
  656.  
  657. input_entry_format::input_entry_format(format_type t, input_entry_format *p)
  658. : entry_format(t), next(p)
  659. {
  660.   separation = -1;
  661.   last_column = 0;
  662.   vline = 0;
  663.   pre_vline = 0;
  664.   equal = 0;
  665. }
  666.  
  667. input_entry_format::~input_entry_format()
  668. {
  669. }
  670.  
  671. void free_input_entry_format_list(input_entry_format *list)
  672. {
  673.   while (list) {
  674.     input_entry_format *tem = list;
  675.     list = list->next;
  676.     delete tem;
  677.   }
  678. }
  679.  
  680. void input_entry_format::debug_print()
  681. {
  682.   for (int i = 0; i < pre_vline; i++)
  683.     putc('|', stderr);
  684.   entry_format::debug_print();
  685.   if (!width.empty()) {
  686.     putc('w', stderr);
  687.     putc('(', stderr);
  688.     put_string(width, stderr);
  689.     putc(')', stderr);
  690.   }
  691.   if (equal)
  692.     putc('e', stderr);
  693.   if (separation >= 0)
  694.     fprintf(stderr, "%d", separation); 
  695.   for (i = 0; i < vline; i++)
  696.     putc('|', stderr);
  697.   if (last_column)
  698.     putc(',', stderr);
  699. }
  700.  
  701. // Return zero if we should give up on this table.
  702. // If this is a continuation format line, current_format will be the current
  703. // format line.
  704.  
  705. format *process_format(table_input &in, options *opt,
  706.                format *current_format = 0)
  707. {
  708.   input_entry_format *list = 0;
  709.   int c = in.get();
  710.   for (;;) {
  711.     int pre_vline = 0;
  712.     int got_format = 0;
  713.     int got_period = 0;
  714.     format_type t;
  715.     for (;;) {
  716.       if (c == EOF) {
  717.     error("end of input while processing format");
  718.     free_input_entry_format_list(list);
  719.     return 0;
  720.       }
  721.       switch (c) {
  722.       case 'n':
  723.       case 'N':
  724.     t = FORMAT_NUMERIC;
  725.     got_format = 1;
  726.     break;
  727.       case 'a':
  728.       case 'A':
  729.     got_format = 1;
  730.     t = FORMAT_ALPHABETIC;
  731.     break;
  732.       case 'c':
  733.       case 'C':
  734.     got_format = 1;
  735.     t = FORMAT_CENTER;
  736.     break;
  737.       case 'l':
  738.       case 'L':
  739.     got_format = 1;
  740.     t = FORMAT_LEFT;
  741.     break;
  742.       case 'r':
  743.       case 'R':
  744.     got_format = 1;
  745.     t = FORMAT_RIGHT;
  746.     break;
  747.       case 's':
  748.       case 'S':
  749.     got_format = 1;
  750.     t = FORMAT_SPAN;
  751.     break;
  752.       case '^':
  753.     got_format = 1;
  754.     t = FORMAT_VSPAN;
  755.     break;
  756.       case '_':
  757.     got_format = 1;
  758.     t = FORMAT_HLINE;
  759.     break;
  760.       case '=':
  761.     got_format = 1;
  762.     t = FORMAT_DOUBLE_HLINE;
  763.     break;
  764.       case '.':
  765.     got_period = 1;
  766.     break;
  767.       case '|':
  768.     pre_vline++;
  769.     break;
  770.       case ' ':
  771.       case '\t':
  772.       case '\n':
  773.     break;
  774.       default:
  775.     if (c == opt->tab_char)
  776.       break;
  777.     error("unrecognised format `%1'", char(c));
  778.     free_input_entry_format_list(list);
  779.     return 0;
  780.       }
  781.       if (got_period)
  782.     break;
  783.       c = in.get();
  784.       if (got_format)
  785.     break;
  786.     }
  787.     if (got_period)
  788.       break;
  789.     list = new input_entry_format(t, list);
  790.     if (pre_vline)
  791.       list->pre_vline = pre_vline;
  792.     int success = 1;
  793.     do {
  794.       switch (c) {
  795.       case 't':
  796.       case 'T':
  797.     c = in.get();
  798.     list->vertical_alignment = entry_modifier::TOP;
  799.     break;
  800.       case 'd':
  801.       case 'D':
  802.     c = in.get();
  803.     list->vertical_alignment = entry_modifier::BOTTOM;
  804.     break;
  805.       case 'u':
  806.       case 'U':
  807.     c = in.get();
  808.     list->stagger = 1;
  809.     break;
  810.       case 'z':
  811.       case 'Z':
  812.     c = in.get();
  813.     list->zero_width = 1;
  814.     break;
  815.       case '0':
  816.       case '1':
  817.       case '2':
  818.       case '3':
  819.       case '4':
  820.       case '5':
  821.       case '6':
  822.       case '7':
  823.       case '8':
  824.       case '9':
  825.     {
  826.       int w = 0;
  827.       do {
  828.         w = w*10 + (c - '0');
  829.         c = in.get();
  830.       } while (c != EOF && csdigit(c));
  831.       list->separation = w;
  832.     }
  833.     break;
  834.       case 'f':
  835.       case 'F':
  836.     do {
  837.       c = in.get();
  838.     } while (c == ' ' || c == '\t');
  839.     if (c == EOF) {
  840.       error("missing font name");
  841.       break;
  842.     }
  843.     if (c == '(') {
  844.       for (;;) {
  845.         c = in.get();
  846.         if (c == EOF || c == ' ' || c == '\t') {
  847.           error("missing `)'");
  848.           break;
  849.         }
  850.         if (c == ')') {
  851.           c = in.get();
  852.           break;
  853.         }
  854.         list->font += char(c);
  855.       }
  856.     }
  857.     else {
  858.       list->font = c;
  859.       char cc = c;
  860.       c = in.get();
  861.       if (!csdigit(cc)
  862.           && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') {
  863.         list->font += char(c);
  864.         c = in.get();
  865.       }
  866.     }
  867.     break;
  868.       case 'v':
  869.       case 'V':
  870.     c = in.get();
  871.     list->vertical_spacing.val = 0;
  872.     list->vertical_spacing.inc = 0;
  873.     if (c == '+' || c == '-') {
  874.       list->vertical_spacing.inc = (c == '+' ? 1 : -1);
  875.       c = in.get();
  876.     }
  877.     if (c == EOF || !csdigit(c)) {
  878.       error("`v' modifier must be followed by number");
  879.       list->vertical_spacing.inc = 0;
  880.     }
  881.     else {
  882.       do {
  883.         list->vertical_spacing.val *= 10;
  884.         list->vertical_spacing.val += c - '0';
  885.         c = in.get();
  886.       } while (c != EOF && csdigit(c));
  887.     }
  888.     if (list->vertical_spacing.val > MAX_VERTICAL_SPACING
  889.         || list->vertical_spacing.val < -MAX_VERTICAL_SPACING) {
  890.       error("unreasonable point size");
  891.       list->vertical_spacing.val = 0;
  892.       list->vertical_spacing.inc = 0;
  893.     }
  894.     break;
  895.       case 'p':
  896.       case 'P':
  897.     c = in.get();
  898.     list->point_size.val = 0;
  899.     list->point_size.inc = 0;
  900.     if (c == '+' || c == '-') {
  901.       list->point_size.inc = (c == '+' ? 1 : -1);
  902.       c = in.get();
  903.     }
  904.     if (c == EOF || !csdigit(c)) {
  905.       error("`p' modifier must be followed by number");
  906.       list->point_size.inc = 0;
  907.     }
  908.     else {
  909.       do {
  910.         list->point_size.val *= 10;
  911.         list->point_size.val += c - '0';
  912.         c = in.get();
  913.       } while (c != EOF && csdigit(c));
  914.     }
  915.     if (list->point_size.val > MAX_POINT_SIZE
  916.         || list->point_size.val < -MAX_POINT_SIZE) {
  917.       error("unreasonable point size");
  918.       list->point_size.val = 0;
  919.       list->point_size.inc = 0;
  920.     }
  921.     break;
  922.       case 'w':
  923.       case 'W':
  924.     c = in.get();
  925.     while (c == ' ' || c == '\t')
  926.       c = in.get();
  927.     if (c == '(') {
  928.       list->width = "";
  929.       c = in.get();
  930.       while (c != ')') {
  931.         if (c == EOF || c == '\n') {
  932.           error("missing `)'");
  933.           free_input_entry_format_list(list);
  934.           return 0;
  935.         }
  936.         list->width += c;
  937.         c = in.get();
  938.       }
  939.       c = in.get();
  940.     }
  941.     else {
  942.       if (c == '+' || c == '-') {
  943.         list->width = char(c);
  944.         c = in.get();
  945.       }
  946.       else
  947.         list->width = "";
  948.       if (c == EOF || !csdigit(c))
  949.         error("bad argument for `w' modifier");
  950.       else {
  951.         do {
  952.           list->width += char(c);
  953.           c = in.get();
  954.         } while (c != EOF && csdigit(c));
  955.       }
  956.     }
  957.     break;
  958.       case 'e':
  959.       case 'E':
  960.     c = in.get();
  961.     list->equal++;
  962.     break;
  963.       case '|':
  964.     c = in.get();
  965.     list->vline++;
  966.     break;
  967.       case 'B':
  968.       case 'b':
  969.     c = in.get();
  970.     list->font = "B";
  971.     break;
  972.       case 'I':
  973.       case 'i':
  974.     c = in.get();
  975.     list->font = "I";
  976.     break;
  977.       case ' ':
  978.       case '\t':
  979.     c = in.get();
  980.     break;
  981.       default:
  982.     if (c == opt->tab_char)
  983.       c = in.get();
  984.     else
  985.       success = 0;
  986.     break;
  987.       }
  988.     } while (success);
  989.     if (list->vline > 2) {
  990.       list->vline = 2;
  991.       error("more than 2 vertical bars between key letters");
  992.     }
  993.     if (c == '\n' || c == ',') {
  994.       c = in.get();
  995.       list->last_column = 1;
  996.     }
  997.   }
  998.   if (c == '.') {
  999.     do {
  1000.       c = in.get();
  1001.     } while (c == ' ' || c == '\t');
  1002.     if (c != '\n') {
  1003.       error("`.' not last character on line");
  1004.       free_input_entry_format_list(list);
  1005.       return 0;
  1006.     }
  1007.   }
  1008.   if (!list) {
  1009.     error("no format");
  1010.     free_input_entry_format_list(list);
  1011.     return 0;
  1012.   }
  1013.   list->last_column = 1;
  1014.   // now reverse the list so that the first row is at the beginning
  1015.   input_entry_format *rev = 0;
  1016.   while (list != 0) {
  1017.     input_entry_format *tem = list->next;
  1018.     list->next = rev;
  1019.     rev = list;
  1020.     list = tem;
  1021.   }
  1022.   list = rev;
  1023.   input_entry_format *tem;
  1024.  
  1025. #if 0
  1026.   for (tem = list; tem; tem = tem->next)
  1027.     tem->debug_print();
  1028.   putc('\n', stderr);
  1029. #endif
  1030.   // compute number of columns and rows
  1031.   int ncolumns = 0;
  1032.   int nrows = 0;
  1033.   int col = 0;
  1034.   for (tem = list; tem; tem = tem->next) {
  1035.     if (tem->last_column) {
  1036.       if (col >= ncolumns)
  1037.     ncolumns = col + 1;
  1038.       col = 0;
  1039.       nrows++;
  1040.     }
  1041.     else
  1042.       col++;
  1043.   }
  1044.   int row;
  1045.   format *f;
  1046.   if (current_format) {
  1047.     if (ncolumns > current_format->ncolumns) {
  1048.       error("cannot increase the number of columns in a continued format");
  1049.       free_input_entry_format_list(list);
  1050.       return 0;
  1051.     }
  1052.     f = current_format;
  1053.     row = f->nrows;
  1054.     f->add_rows(nrows);
  1055.   }
  1056.   else {
  1057.     f = new format(nrows, ncolumns);
  1058.     row = 0;
  1059.   }
  1060.   col = 0;
  1061.   for (tem = list; tem; tem = tem->next) {
  1062.     f->entry[row][col] = *tem;
  1063.     if (col < ncolumns-1) {
  1064.       // use the greatest separation
  1065.       if (tem->separation > f->separation[col]) {
  1066.     if (current_format)
  1067.       error("cannot change column separation in continued format");
  1068.     else
  1069.       f->separation[col] = tem->separation;
  1070.       }
  1071.     }
  1072.     else if (tem->separation >= 0)
  1073.       error("column separation specified for last column");
  1074.     if (tem->equal && !f->equal[col]) {
  1075.       if (current_format)
  1076.     error("cannot change which columns are equal in continued format");
  1077.       else
  1078.     f->equal[col] = 1;
  1079.     }
  1080.     if (!tem->width.empty()) {
  1081.       // use the last width
  1082.       if (!f->width[col].empty() && f->width[col] != tem->width)
  1083.     error("multiple widths for column %1", col+1);
  1084.       f->width[col] = tem->width;
  1085.     }
  1086.     if (tem->pre_vline) {
  1087.       assert(col == 0);
  1088.       f->vline[row][col] = tem->pre_vline;
  1089.     }
  1090.     f->vline[row][col+1] = tem->vline;
  1091.     if (tem->last_column) {
  1092.       row++;
  1093.       col = 0;
  1094.     }
  1095.     else
  1096.       col++;
  1097.   }
  1098.   free_input_entry_format_list(list);
  1099.   for (col = 0; col < ncolumns; col++) {
  1100.     entry_format *e = f->entry[f->nrows-1] + col;
  1101.     if (e->type != FORMAT_HLINE
  1102.     && e->type != FORMAT_DOUBLE_HLINE
  1103.     && e->type != FORMAT_SPAN)
  1104.       break;
  1105.   }
  1106.   if (col >= ncolumns) {
  1107.     error("last row of format is all lines");
  1108.     delete f;
  1109.     return 0;
  1110.   }
  1111.   return f;
  1112. }
  1113.  
  1114. table *process_data(table_input &in, format *f, options *opt)
  1115. {
  1116.   char tab_char = opt->tab_char;
  1117.   int ncolumns = f->ncolumns;
  1118.   int current_row = 0;
  1119.   int format_index = 0;
  1120.   int give_up = 0;
  1121.   enum { DATA_INPUT_LINE, TROFF_INPUT_LINE, SINGLE_HLINE, DOUBLE_HLINE } type;
  1122.   table *tbl = new table(ncolumns, opt->flags, opt->linesize);
  1123.   if (opt->delim[0] != '\0')
  1124.     tbl->set_delim(opt->delim[0], opt->delim[1]);
  1125.   for (;;) {
  1126.     // first determine what type of line this is
  1127.     int c = in.get();
  1128.     if (c == EOF)
  1129.       break;
  1130.     if (c == '.') {
  1131.       int d = in.get();
  1132.       if (d != EOF && csdigit(d)) {
  1133.     in.unget(d);
  1134.     type = DATA_INPUT_LINE;
  1135.       }
  1136.       else {
  1137.     in.unget(d);
  1138.     type = TROFF_INPUT_LINE;
  1139.       }
  1140.     }
  1141.     else if (c == '_' || c == '=') {
  1142.       int d = in.get();
  1143.       if (d == '\n') {
  1144.     if (c == '_')
  1145.       type = SINGLE_HLINE;
  1146.     else
  1147.       type = DOUBLE_HLINE;
  1148.       }
  1149.       else {
  1150.     in.unget(d);
  1151.     type = DATA_INPUT_LINE;
  1152.       }
  1153.     }
  1154.     else {
  1155.       type = DATA_INPUT_LINE;
  1156.     }
  1157.     switch (type) {
  1158.     case DATA_INPUT_LINE:
  1159.       {
  1160.     string input_entry;
  1161.     if (format_index >= f->nrows)
  1162.       format_index = f->nrows - 1;
  1163.     // A format row that is all lines doesn't use up a data line.
  1164.     while (format_index < f->nrows - 1) {
  1165.       for (int c = 0; c < ncolumns; c++) {
  1166.         entry_format *e = f->entry[format_index] + c;
  1167.         if (e->type != FORMAT_HLINE
  1168.         && e->type != FORMAT_DOUBLE_HLINE
  1169.         // Unfortunately tbl treats a span as needing data.
  1170.         // && e->type != FORMAT_SPAN
  1171.         )
  1172.           break;
  1173.       }
  1174.       if (c < ncolumns)
  1175.         break;
  1176.       for (c = 0; c < ncolumns; c++)
  1177.         tbl->add_entry(current_row, c, input_entry,
  1178.                f->entry[format_index] + c, current_filename,
  1179.                current_lineno);
  1180.       tbl->add_vlines(current_row, f->vline[format_index]);
  1181.       format_index++;
  1182.       current_row++;
  1183.     }
  1184.     entry_format *line_format = f->entry[format_index];
  1185.     int col = 0;
  1186.     for (;;) {
  1187.       if (c == tab_char || c == '\n') {
  1188.         int ln = current_lineno;
  1189.         if (c == '\n')
  1190.           --ln;
  1191.         while (col < ncolumns
  1192.            && line_format[col].type == FORMAT_SPAN) {
  1193.           tbl->add_entry(current_row, col, "", &line_format[col],
  1194.                  current_filename, ln);
  1195.           col++;
  1196.         }
  1197.         if (c == '\n' && input_entry.length() == 2
  1198.         && input_entry[0] == 'T' && input_entry[1] == '{') {
  1199.           input_entry = "";
  1200.           ln++;
  1201.           enum {
  1202.         START, MIDDLE, GOT_T, GOT_RIGHT_BRACE, GOT_DOT,
  1203.         GOT_l, GOT_lf, END
  1204.           } state = START;
  1205.           while (state != END) {
  1206.         c = in.get();
  1207.         if (c == EOF)
  1208.           break;
  1209.         switch (state) {
  1210.         case START:
  1211.           if (c == 'T')
  1212.             state = GOT_T;
  1213.           else if (c == '.')
  1214.             state = GOT_DOT;
  1215.           else {
  1216.             input_entry += c;
  1217.             if (c != '\n')
  1218.               state = MIDDLE;
  1219.           }
  1220.           break;
  1221.         case GOT_T:
  1222.           if (c == '}')
  1223.             state = GOT_RIGHT_BRACE;
  1224.           else {
  1225.             input_entry += 'T';
  1226.             input_entry += c;
  1227.             state = c == '\n' ? START : MIDDLE;
  1228.           }
  1229.           break;
  1230.         case GOT_DOT:
  1231.           if (c == 'l')
  1232.             state = GOT_l;
  1233.           else {
  1234.             input_entry += '.';
  1235.             input_entry += c;
  1236.             state = c == '\n' ? START : MIDDLE;
  1237.           }
  1238.           break;
  1239.         case GOT_l:
  1240.           if (c == 'f')
  1241.             state = GOT_lf;
  1242.           else {
  1243.             input_entry += ".l";
  1244.             input_entry += c;
  1245.             state = c == '\n' ? START : MIDDLE;
  1246.           }
  1247.           break;
  1248.         case GOT_lf:
  1249.           if (c == ' ' || c == '\n' || compatible_flag) {
  1250.             string args;
  1251.             input_entry += ".lf";
  1252.             while (c != EOF) {
  1253.               args += c;
  1254.               if (c == '\n')
  1255.             break;
  1256.               c = in.get();
  1257.             }
  1258.             args += '\0';
  1259.             interpret_lf_args(args.contents());
  1260.             // remove the '\0'
  1261.             args.set_length(args.length() - 1);
  1262.             input_entry += args;
  1263.             state = START;
  1264.           }
  1265.           else {
  1266.             input_entry += ".lf";
  1267.             input_entry += c;
  1268.             state = MIDDLE;
  1269.           }
  1270.           break;
  1271.         case GOT_RIGHT_BRACE:
  1272.           if (c == '\n' || c == tab_char)
  1273.             state = END;
  1274.           else {
  1275.             input_entry += 'T';
  1276.             input_entry += '}';
  1277.             input_entry += c;
  1278.             state = c == '\n' ? START : MIDDLE;
  1279.           }
  1280.           break;
  1281.         case MIDDLE:
  1282.           if (c == '\n')
  1283.             state = START;
  1284.           input_entry += c;
  1285.           break;
  1286.         case END:
  1287.         default:
  1288.           assert(0);
  1289.         }
  1290.           }
  1291.           if (c == EOF) {
  1292.         error("end of data in middle of text block");
  1293.         give_up = 1;
  1294.         break;
  1295.           }
  1296.         }
  1297.         if (col >= ncolumns) {
  1298.           if (!input_entry.empty()) {
  1299.         if (c == '\n')
  1300.           in.unget(c);
  1301.         input_entry += '\0';
  1302.         error("excess data entry `%1' discarded",
  1303.               input_entry.contents());
  1304.         if (c == '\n')
  1305.           (void)in.get();
  1306.           }
  1307.         }
  1308.         else
  1309.           tbl->add_entry(current_row, col, input_entry,
  1310.                  &line_format[col], current_filename, ln);
  1311.         col++;
  1312.         if (c == '\n')
  1313.           break;
  1314.         input_entry = "";
  1315.       }
  1316.       else
  1317.         input_entry += c;
  1318.       c = in.get();
  1319.       if (c == EOF)
  1320.         break;
  1321.     }
  1322.     if (give_up)
  1323.       break;
  1324.     input_entry = "";
  1325.     for (; col < ncolumns; col++)
  1326.       tbl->add_entry(current_row, col, input_entry, &line_format[col],
  1327.              current_filename, current_lineno - 1);
  1328.     tbl->add_vlines(current_row, f->vline[format_index]);
  1329.     current_row++;
  1330.     format_index++;
  1331.       }
  1332.       break;
  1333.     case TROFF_INPUT_LINE:
  1334.       {
  1335.     string line;
  1336.     int ln = current_lineno;
  1337.     for (;;) {
  1338.       line += c;
  1339.       if (c == '\n')
  1340.         break;
  1341.       c = in.get();
  1342.       if (c == EOF) {
  1343.         break;
  1344.       }
  1345.     }
  1346.     tbl->add_text_line(current_row, line, current_filename, ln);
  1347.     if (line.length() >= 4 
  1348.         && line[0] == '.' && line[1] == 'T' && line[2] == '&') {
  1349.       format *newf = process_format(in, opt, f);
  1350.       if (newf == 0)
  1351.         give_up = 1;
  1352.       else
  1353.         f = newf;
  1354.     }
  1355.     if (line.length() >= 3
  1356.         && line[0] == '.' && line[1] == 'f' && line[2] == 'f') {
  1357.       line += '\0';
  1358.       interpret_lf_args(line.contents() + 3);
  1359.     }
  1360.       }
  1361.       break;
  1362.     case SINGLE_HLINE:
  1363.       tbl->add_single_hline(current_row);
  1364.       break;
  1365.     case DOUBLE_HLINE:
  1366.       tbl->add_double_hline(current_row);
  1367.       break;
  1368.     default:
  1369.       assert(0);
  1370.     }
  1371.     if (give_up)
  1372.       break;
  1373.   }
  1374.   if (!give_up && current_row == 0) {
  1375.     error("no real data");
  1376.     give_up = 1;
  1377.   }
  1378.   if (give_up) {
  1379.     delete tbl;
  1380.     return 0;
  1381.   }
  1382.   // Do this here rather than at the beginning in case continued formats
  1383.   // change it.
  1384.   for (int i = 0; i < ncolumns - 1; i++)
  1385.     if (f->separation[i] >= 0)
  1386.       tbl->set_column_separation(i, f->separation[i]);
  1387.   for (i = 0; i < ncolumns; i++)
  1388.     if (!f->width[i].empty())
  1389.       tbl->set_minimum_width(i, f->width[i]);
  1390.   for (i = 0; i < ncolumns; i++)
  1391.     if (f->equal[i])
  1392.       tbl->set_equal_column(i);
  1393.   return tbl;
  1394. }
  1395.  
  1396. void process_table(table_input &in)
  1397. {
  1398.   int c;
  1399.   options *opt = 0;
  1400.   format *form = 0;
  1401.   table *tbl = 0;
  1402.   if ((opt = process_options(in)) != 0 
  1403.       && (form = process_format(in, opt)) != 0
  1404.       && (tbl = process_data(in, form, opt)) != 0) {
  1405.     tbl->print();
  1406.     delete tbl;
  1407.   }
  1408.   else {
  1409.     error("giving up on this table");
  1410.     while ((c = in.get()) != EOF)
  1411.       ;
  1412.   }
  1413.   delete opt;
  1414.   delete form;
  1415.   if (!in.ended())
  1416.     error("premature end of file");
  1417. }
  1418.  
  1419. static void usage()
  1420. {
  1421.   fprintf(stderr, "usage: %s [ -vC ] [ files... ]\n", program_name);
  1422.   exit(1);
  1423. }
  1424.  
  1425. int main(int argc, char **argv)
  1426. {
  1427.   program_name = argv[0];
  1428.   static char stderr_buf[BUFSIZ];
  1429.   setbuf(stderr, stderr_buf);
  1430.   int opt;
  1431.   while ((opt = getopt(argc, argv, "vC")) != EOF)
  1432.     switch (opt) {
  1433.     case 'C':
  1434.       compatible_flag = 1;
  1435.       break;
  1436.     case 'v':
  1437.       {
  1438.     extern const char *version_string;
  1439.     fprintf(stderr, "GNU tbl version %s\n", version_string);
  1440.     fflush(stderr);
  1441.     break;
  1442.       }
  1443.     case '?':
  1444.       usage();
  1445.       break;
  1446.     default:
  1447.       assert(0);
  1448.     }
  1449.   printf(".if !\\n(.g .ab GNU tbl requires GNU troff.\n"
  1450.      ".if !dTS .ds TS\n"
  1451.      ".if !dTE .ds TE\n");
  1452.   if (argc > optind) {
  1453.     for (int i = optind; i < argc; i++) 
  1454.       if (argv[i][0] == '-' && argv[i][1] == '\0') {
  1455.     current_filename = "-";
  1456.     current_lineno = 1;
  1457.     printf(".lf 1 -\n");
  1458.     process_input_file(stdin);
  1459.       }
  1460.       else {
  1461.     errno = 0;
  1462.     FILE *fp = fopen(argv[i], "r");
  1463.     if (fp == 0) {
  1464.       current_lineno = -1;
  1465.       error("can't open `%1': %2", argv[i], strerror(errno));
  1466.     }
  1467.     else {
  1468.       current_lineno = 1;
  1469.       current_filename = argv[i];
  1470.       printf(".lf 1 %s\n", current_filename);
  1471.       process_input_file(fp);
  1472.     }
  1473.       }
  1474.   }
  1475.   else {
  1476.     current_filename = "-";
  1477.     current_lineno = 1;
  1478.     printf(".lf 1 -\n");
  1479.     process_input_file(stdin);
  1480.   }
  1481.   if (ferror(stdout) || fflush(stdout) < 0)
  1482.     fatal("output error");
  1483.   exit(0);
  1484. }
  1485.  
  1486.